/*
 * Contributors: Youngjae Kim (youkim@cse.psu.edu)
 *               Aayush Gupta (axg354@cse.psu.edu_
 *   
 * In case if you have any doubts or questions, kindly write to: youkim@cse.psu.edu 
 *
 * This source plays a role as bridiging disksim and flash simulator. 
 * 
 * Request processing flow: 
 *
 *  1. Request is sent to the simple flash device module. 
 *  2. This interface determines FTL type. Then, it sends the request
 *     to the lower layer according to the FTL type. 
 *  3. It returns total time taken for processing the request in the flash. 
 *
 */
/**************************2013-01-29 developed by WeinaJiang*******************
*
*add: vftl algorithm
* 
********************************************************************************/

#include "ssd_interface.h"
#include "disksim_global.h"
#include "dftl.h"
#include "vftl.h"

extern int merge_switch_num;
extern int merge_partial_num;
extern int merge_full_num;
int old_merge_switch_num = 0;
int old_merge_partial_num = 0;
int old_merge_full_num= 0;
int old_flash_gc_read_num = 0;
int old_flash_erase_num = 0;
int req_count_num = 1;
int rqst_cnt;//cache_hit, 
int flag1 = 1;
int count = 0;

int page_num_for_2nd_map_table;

#define MAP_REAL_MAX_ENTRIES 6552// real map table size in bytes
#define MAP_GHOST_MAX_ENTRIES 1640// ghost_num is no of entries chk if this is ok
#define MAP_CLEAN_MAX_ENTRIES 512//clean map table size

#define CACHE_MAX_ENTRIES 300


int ghost_arr[MAP_GHOST_MAX_ENTRIES];
int real_arr[MAP_REAL_MAX_ENTRIES];
int cache_arr[CACHE_MAX_ENTRIES];
int clean_arr[MAP_CLEAN_MAX_ENTRIES];


/***********************************************************************
  Variables for statistics    
 ***********************************************************************/
unsigned int cnt_read = 0;
unsigned int cnt_write = 0;
unsigned int cnt_delete = 0;
unsigned int cnt_evict_from_flash = 0;
unsigned int cnt_evict_into_disk = 0;
unsigned int cnt_fetch_miss_from_disk = 0;
unsigned int cnt_fetch_miss_into_flash = 0;

double sum_of_queue_time = 0.0;
double sum_of_service_time = 0.0;
double sum_of_response_time = 0.0;
unsigned int total_num_of_req = 0;

/***********************************************************************
  Mapping table
 ***********************************************************************/
int real_min = -1;
int real_max = 0;

int clean_min = -1;
int clean_max = 0;


/***********************************************************************
  Cache
 ***********************************************************************/
int cache_min = -1;
int cache_max = 0;

// Interface between disksim & fsim 

void reset_flash_stat()
{
  flash_read_num = 0;
  flash_write_num = 0;
  flash_gc_read_num = 0;
  flash_gc_write_num = 0; 
  flash_erase_num = 0;
  flash_oob_read_num = 0;
  flash_oob_write_num = 0; 
}

FILE *fp_flash_stat;
FILE *fp_gc;
FILE *fp_gc_timeseries;
double gc_di =0 ,gc_ti=0;


double calculate_delay_flash()
{
  double delay;
  double read_delay, write_delay;
  double erase_delay;
  double gc_read_delay, gc_write_delay;
  double oob_write_delay, oob_read_delay;

  oob_read_delay  = (double)OOB_READ_DELAY  * flash_oob_read_num;
  oob_write_delay = (double)OOB_WRITE_DELAY * flash_oob_write_num;

  read_delay     = (double)READ_DELAY  * flash_read_num; 
  write_delay    = (double)WRITE_DELAY * flash_write_num; 
  erase_delay    = (double)ERASE_DELAY * flash_erase_num; 

  gc_read_delay  = (double)GC_READ_DELAY  * flash_gc_read_num; 
  gc_write_delay = (double)GC_WRITE_DELAY * flash_gc_write_num; 


  delay = read_delay + write_delay + erase_delay + gc_read_delay + gc_write_delay + 
    oob_read_delay + oob_write_delay;

  if( flash_gc_read_num > 0 || flash_gc_write_num > 0 || flash_erase_num > 0 ) {
    gc_ti += delay;
  }
  else {
    gc_di += delay;
  }

  if(warm_done == 1){
    fprintf(fp_gc_timeseries, "%d\t%d\t%d\t%d\t%d\t%d\n", 
      req_count_num, merge_switch_num - old_merge_switch_num, 
      merge_partial_num - old_merge_partial_num, 
      merge_full_num - old_merge_full_num, 
      flash_gc_read_num,
      flash_erase_num);

    old_merge_switch_num = merge_switch_num;
    old_merge_partial_num = merge_partial_num;
    old_merge_full_num = merge_full_num;
    req_count_num++;
  }

  reset_flash_stat();

  return delay;
}


/***********************************************************************
  Initialize Flash Drive 
  ***********************************************************************/

void initFlash()
{
  blk_t total_blk_num;
  blk_t total_util_blk_num;
  blk_t total_extr_blk_num;

  // total number of sectors    
  total_util_sect_num  = flash_numblocks;
  total_extra_sect_num = flash_extrblocks;
  total_sect_num = total_util_sect_num + total_extra_sect_num; 

  // total number of blocks 
  total_blk_num      = total_sect_num / SECT_NUM_PER_BLK;     // total block number
  total_util_blk_num = total_util_sect_num / SECT_NUM_PER_BLK;    // total unique block number

  global_total_blk_num = total_util_blk_num;

  total_extr_blk_num = total_blk_num - total_util_blk_num;        // total extra block number

  ASSERT(total_extr_blk_num != 0);

  if (nand_init(total_blk_num, 3) < 0) {
    EXIT(-4); 
  }

  switch(ftl_type){

    // pagemap
    case 1: ftl_op = pm_setup(); break;
    // blockmap
    //case 2: ftl_op = bm_setup(); break;
    // o-pagemap 
    case 3: ftl_op = opm_setup(); break;
    // fast
    case 4: ftl_op = lm_setup(); break;
    //vftl-pagemap
    case 5: ftl_op = vftl_setup();break;

    default: break;
  }

  ftl_op->init(total_util_blk_num, total_extr_blk_num);

  nand_stat_reset();
}

// record the erase counts of each block
void printWearout(FILE *outFP)  
{
  int i;
  fprintf(outFP, "\n");
  fprintf(outFP, "Wear STATISTICS\n");
  fprintf(outFP, "------------------------------------------------------------\n");

  for(i = 0; i<nand_blk_num; i++)
  {
    fprintf(outFP, "%d %d\n", i, nand_blk[i].state.ec);
  }
  fprintf(outFP, "------------------------------------------------------------\n");

}

void printCacheState(FILE *outFP)
{

    fprintf(outFP, "****************************************************************************\n");
    fprintf(outFP, "                               Flash Memory Statistics                      \n");
    fprintf(outFP, "****************************************************************************\n");
    fprintf(outFP, "\n");
    fprintf(outFP, "Cache STATISTICS\n");
    fprintf(outFP, "------------------------------------------------------------\n");
    fprintf(outFP, " cache_hit (#): %8u   ", cache_hit);
    fprintf(outFP, " flash_hit (#): %8u   ", flash_hit);
    fprintf(outFP, " read_count (#): %8u   ", read_count);
    fprintf(outFP, " write_count (#): %8u\n", write_count);
    fprintf(outFP, " evict (#): %8u   ", evict);
    fprintf(outFP, " delay_flash_update (#): %8u   ", delay_flash_update);
    fprintf(outFP, " save_count (#): %8u\n", save_count);
    fprintf(outFP, " update_evict (#): %8u   ", update_evict);
  //fprintf(outFP, " swicth_num (#): %8u\n", swicth_num); 
    fprintf(outFP, "------------------------------------------------------------\n");


}


void endFlash()
{
  printCacheState(outputfile);
  nand_stat_print(outputfile);
  printWearout(outputfile);
  ftl_op->end;
  nand_end();
}  

/***********************************************************************
  Send request (lsn, sector_cnt, operation flag)
  ***********************************************************************/

void send_flash_request(int start_blk_no, int block_cnt, int operation, int mapdir_flag)
{
	int size;
	//size_t (*op_func)(sect_t lsn, size_t size);
	size_t (*op_func)(sect_t lsn, size_t size, int mapdir_flag);

        if((start_blk_no + block_cnt) >= total_util_sect_num){
          printf("start_blk_no: %d, block_cnt: %d, total_util_sect_num: %d\n", 
              start_blk_no, block_cnt, total_util_sect_num);
          exit(0);
        }

	switch(operation){

	//write
	case 0:

		op_func = ftl_op->write;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;
	//read
	case 1:


		op_func = ftl_op->read;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;

	default: 
		break;
	}
}

void find_real_max()
{
  int i; 

  for(i=0;i < MAP_REAL_MAX_ENTRIES; i++) {
      if(opagemap[real_arr[i]].map_age > opagemap[real_max].map_age) {
          real_max = real_arr[i];
      }
  }
}



void find_clean_min_vftl()
{
	int i,index; 
	  int temp = 99999999;
	
	  for(i=0; i < MAP_CLEAN_MAX_ENTRIES; i++) {
			if(vftl_pagemap[clean_arr[i]].map_age <= temp) {
				clean_min = clean_arr[i];
				temp = vftl_pagemap[clean_arr[i]].map_age;
				index = i;
			}
	  }  

	

}


void find_real_max_vftl()
{
	int i; 

  	for(i=0;i < MAP_REAL_MAX_ENTRIES; i++) {
      		if(vftl_pagemap[real_arr[i]].map_age > vftl_pagemap[real_max].map_age) {
          		real_max = real_arr[i];
      		}
  	}

}



void find_real_min()
{
  
  int i,index; 
  int temp = 99999999;

  for(i=0; i < MAP_REAL_MAX_ENTRIES; i++) {
        if(opagemap[real_arr[i]].map_age <= temp) {
            real_min = real_arr[i];
            temp = opagemap[real_arr[i]].map_age;
            index = i;
        }
  }    
}

void find_real_min_vftl()
{
  	int i,index; 
  	int temp = 99999999;

  	for(i=0; i < MAP_REAL_MAX_ENTRIES; i++) {
        	if(vftl_pagemap[real_arr[i]].map_age <= temp) {
            		real_min = real_arr[i];
            		temp = vftl_pagemap[real_arr[i]].map_age;
            		index = i;
        	}
  	} 
}

int find_min_clean_ghost_entry()
{	
	int i;

	int ghost_min_clean = 0;
	int temp = 99999999;

	for(i = 0; i < MAP_GHOST_MAX_ENTRIES; i++){
		if(vftl_pagemap[ghost_arr[i]].update == 1)
			break;
		if(vftl_pagemap[ghost_arr[i]].map_age <= temp){
			ghost_min_clean = ghost_arr[i];
			temp = vftl_pagemap[ghost_arr[i]].map_age;
		}
	}

	return ghost_min_clean;
	
} 

int find_min_dirty_ghost_entry()
{
	int i;

	int ghost_min_dirty = 0;
	int temp = 99999999;

	for(i = 0; i < MAP_GHOST_MAX_ENTRIES; i++){
		if(vftl_pagemap[ghost_arr[i]].update == 1){
			if(vftl_pagemap[ghost_arr[i]].map_age <= temp){
				ghost_min_dirty = ghost_arr[i];
				temp = vftl_pagemap[ghost_arr[i]].map_age;
			}
		}
	}
	return ghost_min_dirty;

}

int find_min_ghost_entry()
{
  int i; 

  int ghost_min = 0;
  int temp = 99999999; 

  for(i=0; i < MAP_GHOST_MAX_ENTRIES; i++) {
    if( opagemap[ghost_arr[i]].map_age <= temp) {
      ghost_min = ghost_arr[i];
      temp = opagemap[ghost_arr[i]].map_age;
    }
  }
  return ghost_min;
}


void init_arr()
{

  int i;
  for( i = 0; i < MAP_REAL_MAX_ENTRIES; i++) {
      real_arr[i] = -1;
  }
  for( i = 0; i < MAP_GHOST_MAX_ENTRIES; i++) {
      ghost_arr[i] = -1;
  }
  for( i = 0; i < CACHE_MAX_ENTRIES; i++) {
      cache_arr[i] = -1;
  }
  for( i = 0; i< MAP_CLEAN_MAX_ENTRIES; i++){
	clean_arr[i] = -1;
  }

}

int search_table(int *arr, int size, int val) 
{
    int i;
    for(i =0 ; i < size; i++) {
        if(arr[i] == val) {
            return i;
        }
    }

    printf("shouldnt come here for search_table()=%d,%d",val,size);
    for( i = 0; i < size; i++) {
      if(arr[i] != -1) {
        printf("arr[%d]=%d ",i,arr[i]);
      }
    }
    exit(1);
    return -1;
}

int find_free_pos( int *arr, int size)
{
    int i;
    for(i = 0 ; i < size; i++) {
        if(arr[i] == -1) {
            return i;
        }
    } 
    printf("shouldnt come here for find_free_pos()");
    exit(1);
    return -1;
}

void find_min_cache()
{
  int i; 
  int temp = 999999;

  for(i=0; i < CACHE_MAX_ENTRIES ;i++) {
      if(opagemap[cache_arr[i]].cache_age <= temp ) {
          cache_min = cache_arr[i];
          temp = opagemap[cache_arr[i]].cache_age;
      }
  }
}

/*int clean_page_write()
{
	
	if(curr_clean_maptable_no == -1){
		printf("there is on clean page in the flash\n.");
		
		
		
		}
	else {
		//clean_page be invalidate;
		}
		
		//write the new clean_page;
			
			
}*/


int youkim_flag1=0;

double callFsim(unsigned int secno, int scount, int operation)
{
  double delay; 
  int bcount;
  unsigned int blkno; // pageno for page based FTL
  int cnt,z; int min_ghost;

  int pos=-1,pos_real=-1,pos_ghost=-1;

  int demand_trans_pagemap_no = -1;
  int ghost_min_clean_entry = -1;
  int ghost_min_dirty_entry = -1;
  int same_trans_entry_num =0;
  int free_clean_pos = -1;
  int min_clean_pos = -1;
  

  if(ftl_type == 1){ }

  if(ftl_type == 3) {
      page_num_for_2nd_map_table = (opagemap_num / MAP_ENTRIES_PER_PAGE);
    
      if(youkim_flag1 == 0 ) {
        youkim_flag1 = 1;
        init_arr();
      }

      if((opagemap_num % MAP_ENTRIES_PER_PAGE) != 0){
        page_num_for_2nd_map_table++;
      }
  }
      
  // page based FTL 
  if(ftl_type == 1 ) { 
    blkno = secno / 4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }  
  // block based FTL 
  else if(ftl_type == 2){
    blkno = secno/4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }
  // o-pagemap scheme
  else if(ftl_type == 3 ) { 
    blkno = secno / 4;
    blkno += page_num_for_2nd_map_table;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }  
  // FAST scheme
  else if(ftl_type == 4){
    blkno = secno/4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }
  else if(ftl_type == 5){
    blkno = secno/4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;

  }

  cnt = bcount;

  switch(operation)
  {
    //write/read
    case 0:
    case 1:

    while(cnt > 0)
    {
          cnt--;

        // page based FTL
        if(ftl_type == 1){
          send_flash_request(blkno*4, 4, operation, 1); 
          blkno++;
        }

        // blck based FTL
        else if(ftl_type == 2){
          send_flash_request(blkno*4, 4, operation, 1); 
          blkno++;
        }

        // opagemap ftl scheme
        else if(ftl_type == 3)
        {

          /************************************************
            primary map table 
          *************************************************/
          //1. pagemap in SRAM 

            rqst_cnt++;
          if((opagemap[blkno].map_status == MAP_REAL) || (opagemap[blkno].map_status == MAP_GHOST))
          {
            cache_hit++;

	    //this blkno's visit age add 1
            opagemap[blkno].map_age++;

            //If the blkno was in the ghost_arr before this time
            if(opagemap[blkno].map_status == MAP_GHOST){
	      //find the min visit entry (map_age)of the real array
              if ( real_min == -1 ) {
                real_min = 0;
                find_real_min();
              } 
              //if blkno's map_age is larger than all of the real entries' 
	      //then the blkno keep in the real array
              if(opagemap[real_min].map_age <= opagemap[blkno].map_age) 
              {
                find_real_min();  // probably the blkno is the new real_min alwaz
                opagemap[blkno].map_status = MAP_REAL;//change blkno status to be real
                opagemap[real_min].map_status = MAP_GHOST;//change the real_min status to be ghost

		//find the blkno's pos in ghost array,then clean this pos's value to -1
                pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,blkno);
                ghost_arr[pos_ghost] = -1;
                
		//find the real_min's pos in real_arr,then clean this pos's value to -1
                pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                real_arr[pos_real] = -1;

		//value the real_arr's pos to blkno
		//value the ghost_arr's pos to real_min
                real_arr[pos_real]   = blkno; 
                ghost_arr[pos_ghost] = real_min; 
              }
            }
	    //if the blkno was in the real_arr before this time
            else if(opagemap[blkno].map_status == MAP_REAL) 
            {
	      //if this real_arr have no entry before this time, find the real_max
              if ( real_max == -1 ) {
                real_max = 0;
                find_real_max();
                printf("Never happend\n");
              }
	      //if this blkno is larger than the old real_max,this blkno is new real_max 
              if(opagemap[real_max].map_age <= opagemap[blkno].map_age)
              {
                real_max = blkno;
              }  
            }
	    // blkno was neither in real_arr nor ghost_arr,this fact shouldnt happen
            else {
              printf("forbidden/shouldnt happen real =%d , ghost =%d\n",MAP_REAL,MAP_GHOST);
            }
          }


          //2. opagemap not in SRAM 
          else
          {
            //if map table in SRAM is full
	    //if real_arr is full
            if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0)
            {
	      //if ghost_arr is also full, then need to evict a ghost-arr to flash	
              if((MAP_GHOST_MAX_ENTRIES - MAP_GHOST_NUM_ENTRIES) == 0)
              { //evict one entry from ghost cache to DRAM or Disk, delay = DRAM or disk write, 1 oob write for invalidation 
                min_ghost = find_min_ghost_entry();
                  evict++;
		//evict entry min_ghost has been update, then it's new value need to be write into the flash
                if(opagemap[min_ghost].update == 1) {
                  update_reqd++;
                  opagemap[min_ghost].update = 0;
                  send_flash_request(((min_ghost-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2);   // read from 2nd mapping table then update it

                  send_flash_request(((min_ghost-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 0, 2);   // write into 2nd mapping table 
                } 
                opagemap[min_ghost].map_status = MAP_INVALID;//this lba's ppn is invalid

                MAP_GHOST_NUM_ENTRIES--;//ghost_arr decrease 1

                //evict one entry from real cache to ghost cache 
                MAP_REAL_NUM_ENTRIES--;
                MAP_GHOST_NUM_ENTRIES++;
                find_real_min();
                opagemap[real_min].map_status = MAP_GHOST;

                pos = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,min_ghost);
                ghost_arr[pos]=-1;

                
                ghost_arr[pos]= real_min;
                
                pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                real_arr[pos]=-1;
              }
              else{
		//if ghost_arr is not full
                //evict one entry from real cache to ghost cache 
                MAP_REAL_NUM_ENTRIES--;
                find_real_min();
                opagemap[real_min].map_status = MAP_GHOST;
               
                pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                real_arr[pos]=-1;

                pos = find_free_pos(ghost_arr,MAP_GHOST_MAX_ENTRIES);
                ghost_arr[pos]=real_min;
                
                MAP_GHOST_NUM_ENTRIES++;
              }
            }
	    //if blkno is not in sram, search in flash
            flash_hit++;
            send_flash_request(((blkno-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2);   // read from 2nd mapping table
	    //this blkno need to put into real_arr
            opagemap[blkno].map_status = MAP_REAL;
	    //new put real_arr's map_age = real_max's +1 ???
            opagemap[blkno].map_age = opagemap[real_max].map_age + 1;
            real_max = blkno;
            MAP_REAL_NUM_ENTRIES++;
            //find the real_arr's free pos, put blkno in it
            pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
            real_arr[pos] = blkno;
          }

         //comment out the next line when using cache
          if(operation==0){
            write_count++;
            opagemap[blkno].update = 1;
          }
          else
             read_count++;

          send_flash_request(blkno*4, 4, operation, 1); 
          blkno++;
        }

        // FAST scheme  
        else if(ftl_type == 4){ 

          if(operation == 0){
            write_count++;
          }
          else read_count++;

          send_flash_request(blkno*4, 4, operation, 1); //cache_min is a page for page baseed FTL
          blkno++;
        }

	// VFTL scheme
        else if(ftl_type == 5){
            //1. if in SRAM
            if((vftl_pagemap[blkno].map_status == MAP_REAL) ||
               (vftl_pagemap[blkno].map_status == MAP_GHOST) ||
               (vftl_pagemap[blkno].map_status == MAP_CLEAN) )
            {
                cache_hit++;
                vftl_pagemap[blkno].map_age++;

				//MAP_REAL
                if(vftl_pagemap[blkno].map_status == MAP_REAL) {
					if ( real_max == -1 ) {
						real_max = 0;
                        find_real_max_vftl();
                        printf("Never happend\n");
						}
					if(vftl_pagemap[real_max].map_age <= vftl_pagemap[blkno].map_age){
						real_max = blkno;
						}
                    }

				//MAP_GHOST
                if(vftl_pagemap[blkno].map_status == MAP_GHOST){
					find_real_min_vftl();
					
					//switch the position of two entry
					pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,blkno);
                    ghost_arr[pos_ghost] = -1;
					
                    pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                    real_arr[pos_real] = -1;

                    real_arr[pos_real]   = blkno;
                    ghost_arr[pos_ghost] = real_min;

                    vftl_pagemap[blkno].map_status = MAP_REAL;
                    vftl_pagemap[real_min].map_status = MAP_GHOST;

                    real_max = blkno;
					}

				//MAP_CLEAN
                if(vftl_pagemap[blkno].map_status == MAP_CLEAN){
                    //reset the clean_arr[blkno]
                    for(i = 0; i< MAP_CLEAN_MAX_ENTRIES;i++){
						if(clean_arr[i] == blkno){
                            evict_clean_pos = i;
                            break;
							}
                        clean_arr[evict_clean_pos] = -1;
                        MAP_CLEAN_NUM_ENTRIES--;
						}

                        //evict one clean entry of ghost_arr into clean_arr;
                        ghost_min_clean_entry = find_min_clean_ghost_entry();//page no

                        //if there is no clean entry in the ghost_arr
                        if(ghost_min_clean_entry == 0){
							
							//evict++;
							
							//find the dirty_min in the ghost_arr
							ghost_min_dirty_entry = find_min_dirty_ghost_entry();
                            //write back the dirty entry
                            same_trans_entry_num = 0;
							
                            demand_trans_pagemap_no = ghost_min_dirty_entry / MAP_ENTRIES_PER_PAGE;
                            //real_arr and ghost_arr all in the same translation be updated 
                            for (i = 0; i < MAP_REAL_MAX_ENTRIES; i++){
								if((real_arr[i] / MAP_ENTRIES_PER_PAGE) == demand_trans_pagemap_no 
									&& vftl_pagemap[real_arr[i]].update == 1){
									vftl_pagemap[real_arr[i]].update = 0;
                                    vftl_pagemap[real_arr[i]].map_status = MAP_INVALID;
									//real_arr[i] = -1;
									same_trans_entry_num++;
									update_evict++;
									}
								}
							for (i=0; i<MAP_GHOST_MAX_ENTRIES; i++){
								if (ghost_arr[i]/MAP_ENTRIES_PER_PAGE == demand_trans_pagemap_no 
									&& vftl_pagemap[ghost_arr[i]].update == 1) {
									vftl_pagemap[ghost_arr[i]].update = 0;
									vftl_pagemap[ghost_arr[i]].map_status = MAP_INVALID;
									//ghost_arr[i] = -1;
                                    same_trans_entry_num++;
                                    update_evict++;
									}
								}
							//write back this dirty translation page---need to finish;
							send_flash_request(demand_trans_pagemap_no);
							
							//find_real_min_vftl();

                            pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_dirty_entry);
                            ghost_arr[pos_ghost] = -1;

                            //pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                            //real_arr[pos_real] = -1;
                            //real_arr[pos_real] = blkno;
                            //ghost_arr[pos_ghost] = real_min;
					
                            //vftl_pagemap[blkno].map_status = MAP_REAL;
                            //vftl_pagemap[real_min].map_status = MAP_GHOST;
							MAP_GHOST_NUM_ENTRIES--;
							}
						
						if(ghost_min_clean_entry != 0){
							pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_clean_entry);
							ghost_arr[pos_ghost] = -1;
							clean_arr[evict_clean_pos] = ghost_min_clean_entry;
							vftl_pagemap[ghost_min_clean_entry].map_status = MAP_CLEAN;
							MAP_CLEAN_NUM_ENTRIES++;
							}
						
						find_real_min_vftl();
						pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
						
						//real_arr[pos_real] = -1;
						real_arr[pos_real] = blkno;
                        ghost_arr[pos_ghost] = real_min;
                            
                        vftl_pagemap[blkno].map_status = MAP_REAL;
                        vftl_pagemap[real_min].map_status = MAP_GHOST;
                     
						}
				}
			//2. hit the SRAM,and hit the Spcmt ,is  search the spatial request
			else if(blkno / MAP_ENTRIES_PER_PAGE == curr_trans_maptable_no ){
				
				cache_hit++;
				if ( MIN_SEQ_REQ_NUM >= bcount ){
					if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0){
						if((MAP_GHOST_MAX_ENTRIES - MAP_GHOST_NUM_ENTRIES) == 0){
							ghost_min_clean_entry = find_min_clean_ghost_entry();
							
							if(ghost_min_clean_entry != 0){
								//there is a clean_entry in the ghost put into the clean_arr
							    //if clean_arr is full
								if(MAP_CLEAN_MAX_ENTRIES - MAP_CLEAN_NUM_ENTRIES == 0){
									//evict a oldest clean entry
									find_clean_min_vftl();
									min_clean_pos = search_table(clean_arr,MAP_CLEAN_MAX_ENTRIES,clean_min);
									clean_arr[min_clean_pos] = -1;
									MAP_CLEAN_NUM_ENTRIES--;
									}
								
								//wirte the new ghost_min_clean 
								vftl_pagemap[ghost_min_clean_entry].map_status = MAP_CLEAN;
								clean_max = ghost_min_clean_entry;
								MAP_CLEAN_NUM_ENTRIES++;
								free_clean_pos = find_free_pos(clean_arr,MAP_CLEAN_MAX_ENTRIES);
								clean_arr[free_clean_pos] = ghost_min_clean_entry;
			
								find_real_min_vftl(); //find the real_min
								pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_clean_entry);
							    ghost_arr[pos_ghost] = -1;

								pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
								real_arr[pos_real] = -1;
								//real_arr[pos_real] = blkno;
								
								ghost_arr[pos_ghost] = real_min;
                               
                               // vftl_pagemap[blkno].map_status = MAP_REAL;
                                vftl_pagemap[real_min].map_status = MAP_GHOST;
								}
							else {
								//if there is no clean_entry in the ghost
								//find the dirty_min in the ghost_arr
								ghost_min_dirty_entry = find_min_dirty_ghost_entry();
								same_trans_entry_num = 0;
								demand_trans_pagemap_no = ghost_min_dirty_entry / MAP_ENTRIES_PER_PAGE;
								//real_arr and ghost_arr update all
								for (i = 0; i < MAP_REAL_MAX_ENTRIES; i++){
									if((real_arr[i] / MAP_ENTRIES_PER_PAGE) == demand_trans_pagemap_no
										&& vftl_pagemap[real_arr[i]].update == 1){
										vftl_pagemap[real_arr[i]].update = 0;
										vftl_pagemap[real_arr[i]].map_status = MAP_INVALID;
										//real_arr[i] = -1;
										same_trans_entry_num++;
										update_evict++;
										}
									}
								for (i=0; i<MAP_GHOST_MAX_ENTRIES; i++) {
									if (ghost_arr[i]/MAP_ENTRIES_PER_PAGE == demand_trans_pagemap_no 
										&& vftl_pagemap[ghost_arr[i]].update == 1) {
										vftl_pagemap[ghost_arr[i]].update = 0;
										vftl_pagemap[ghost_arr[i]].map_status = MAP_INVALID;
										//ghost_arr[i] = -1;
										same_trans_entry_num++;
										update_evict++;
										}
									}
								//ghost_min_dirty write back to flash.
								send_flash_request(((ghost_min_dirty_entry - page_num_for_2nd_map_table - 1)/MAP_ENTRIES_PER_PAGE)*4,4,1,2);
								send_flash_request(((ghost_min_dirty_entry - page_num_for_2nd_map_table - 1)/MAP_ENTRIES_PER_PAGE)*4,4,0,2);
								MAP_GHOST_NUM_ENTRIES --;
								
								find_real_min_vftl();
								pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_dirty_entry);
								ghost_arr[pos_ghost] = -1;

								pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
								real_arr[pos_real] = -1;
								//real_arr[pos_real] = blkno;
								ghost_arr[pos_ghost] = real_min;

								//vftl_pagemap[blkno].map_status = MAP_REAL;
								vftl_pagemap[real_min].map_status = MAP_GHOST;
								MAP_GHOST_NUM_ENTRIES ++;
								}
							}
						//ghost_arr is not full
						find_real_min_vftl();
						pos = find_free_pos(ghost_arr,MAP_GHOST_MAX_ENTRIES);
						pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
						real_arr[pos_real] = -1;
						ghost_arr[pos] = real_min;
						//vftl_pagemap[blkno].map_status = MAP_REAL;
						vftl_pagemap[real_min].map_status = MAP_GHOST;
						MAP_GHOST_NUM_ENTRIES++;
						}
					//real_arr is not full
					pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
					real_arr[pos] = blkno;
					real_max = blkno;
					vftl_pagemap[blkno].map_age = vftl_pagemap[real_max].map_age + 1;
					vftl_pagemap[blkno].map_status = MAP_REAL;
					MAP_REAL_NUM_ENTRIES++;
					}
				// bcount >= thresthold, keep buffer
				}
			else {
				
				flash_hit++;
				//there is no match in the clean_page,evict ghost entry to put new entry
				if ( MIN_SEQ_REQ_NUM >= bcount ){
					if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0){
						if((MAP_GHOST_MAX_ENTRIES - MAP_GHOST_NUM_ENTRIES) == 0){
							ghost_min_clean_entry = find_min_clean_ghost_entry();
							if(ghost_min_clean_entry != 0){
								//there is a clean_entry in the ghost put into the clean_arr
								//if clean_arr is full
								if(MAP_CLEAN_MAX_ENTRIES - MAP_CLEAN_NUM_ENTRIES == 0){
									find_clean_min_vftl();
									min_clean_pos = search_table(clean_arr,MAP_CLEAN_MAX_ENTRIES,clean_min);
									clean_arr[min_clean_pos] = -1;
									MAP_CLEAN_NUM_ENTRIES--;
									}
								//wirte the new ghost_min_clean 
								vftl_pagemap[ghost_min_clean_entry].map_status = MAP_CLEAN;
								clean_max = ghost_min_clean_entry;
								MAP_CLEAN_NUM_ENTRIES++;
								free_clean_pos = find_free_pos(clean_arr,MAP_CLEAN_MAX_ENTRIES);
								clean_arr[free_clean_pos] = ghost_min_clean_entry;
			
								find_real_min_vftl(); //find the real_min
								pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_clean_entry);
								ghost_arr[pos_ghost] = -1;
	
								pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
								real_arr[pos_real] = -1;
								//real_arr[pos_real] = blkno;
									
								ghost_arr[pos_ghost] = real_min;
            	                   
            	                // vftl_pagemap[blkno].map_status = MAP_REAL;
                	            vftl_pagemap[real_min].map_status = MAP_GHOST;
								}
							else {
								//if there is no clean_entry in the ghost
								//find the dirty_min in the ghost_arr
								ghost_min_dirty_entry = find_min_dirty_ghost_entry();
								same_trans_entry_num = 0;
								demand_trans_pagemap_no = ghost_min_dirty_entry / MAP_ENTRIES_PER_PAGE;
								//real_arr and ghost_arr update all
								for (i = 0; i < MAP_REAL_MAX_ENTRIES; i++){
									if((real_arr[i] / MAP_ENTRIES_PER_PAGE) == demand_trans_pagemap_no
										&& vftl_pagemap[real_arr[i]].update == 1){
										vftl_pagemap[real_arr[i]].update = 0;
										vftl_pagemap[real_arr[i]].map_status = MAP_INVALID;
										//real_arr[i] = -1;
										same_trans_entry_num++;
										update_evict++;
										}
									}
								for (i=0; i<MAP_GHOST_MAX_ENTRIES; i++) {
									if (ghost_arr[i]/MAP_ENTRIES_PER_PAGE == demand_trans_pagemap_no 
										&& vftl_pagemap[ghost_arr[i]].update == 1) {
										vftl_pagemap[ghost_arr[i]].update = 0;
										vftl_pagemap[ghost_arr[i]].map_status = MAP_INVALID;
										//ghost_arr[i] = -1;
										same_trans_entry_num++;
										update_evict++;
										}
									}
								//ghost_min_dirty write back to flash.
								send_flash_request(((ghost_min_dirty_entry - page_num_for_2nd_map_table - 1)/MAP_ENTRIES_PER_PAGE)*4,4,1,2);
								send_flash_request(((ghost_min_dirty_entry - page_num_for_2nd_map_table - 1)/MAP_ENTRIES_PER_PAGE)*4,4,0,2);
								MAP_GHOST_NUM_ENTRIES --;
									
								find_real_min_vftl();
								pos_ghost = search_table(ghost_arr,MAP_GHOST_MAX_ENTRIES,ghost_min_dirty_entry);
								ghost_arr[pos_ghost] = -1;
	
								pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
								real_arr[pos_real] = -1;
								//real_arr[pos_real] = blkno;
								ghost_arr[pos_ghost] = real_min;
								//vftl_pagemap[blkno].map_status = MAP_REAL;
								vftl_pagemap[real_min].map_status = MAP_GHOST;
								MAP_GHOST_NUM_ENTRIES ++;
								}
								}
                       	    // ghost_arr is not full
                       	    find_real_min_vftl();
                       	    pos = find_free_pos(ghost_arr,MAP_GHOST_MAX_ENTRIES);
                       	    pos_real = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
                       	    real_arr[pos_real] = -1;
                        	real_arr[pos_real] = blkno;
                           	ghost_arr[pos] = real_min;

	                        vftl_pagemap[blkno].map_status = MAP_REAL;
                            vftl_pagemap[real_min].map_status = MAP_GHOST;
        	                MAP_GHOST_NUM_ENTRIES++;
	
                            }
               	 	    //real_arr is not full
						pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
						real_arr[pos] = blkno;
						real_max = blkno;
						vftl_pagemap[blkno].map_age = vftl_pagemap[real_max].map_age + 1;
						vftl_pagemap[blkno].map_status = MAP_REAL;
						vftl_pagemap[blkno].local = 1;
						MAP_REAL_NUM_ENTRIES++;
						}
				else if(MIN_SEQ_REQ_NUM < bcount){
					demand_trans_pagemap_no = blkno / MAP_ENTRIES_PER_PAGE;

					if(demand_trans_pagemap_no != curr_trans_maptable_no){
						if(trans_page_dir[curr_trans_maptable_no].update == 1){
							trans_page_maptable_write(curr_trans_maptable_no * SECT_NUM_PER_PAGE,SECT_NUM_PER_PAGE,2);
							}
						trans_page_maptable_read(demand_trans_pagemap_no* SECT_NUM_PER_PAGE,SECT_NUM_PER_PAGE,2);
						}
					
					
					}
				//wirte
				if(operation==0){
					if((vftl_pagemap[blkno].map_status == MAP_REAL) ||
						(vftl_pagemap[blkno].map_status == MAP_GHOST)){
						vftl_pagemap[blkno].update = 1;
						}
					else {
						if(MIN_SEQ_REQ_NUM >= bcount){
							vftl_pagemap[blkno].update = 1;
							}	
						}
					}

				if((vftl_pagemap[blkno].cache_status == MAP_REAL) || 
					(vftl_pagemap[blkno].cache_status == MAP_GHOST) || 
					(vftl_pagemap[blkno].cache_status == MAP_CLEAN)){
					curr_trans_mpatable_update_mark = 0;
					}
				else {
					if(MIN_SEQ_REQ_NUM >= bcount){
						curr_trans_mpatable_update_mark = 0;
						}
					else {
						curr_trans_mpatable_update_mark = 1;
						}
					}
			
				if(operation == 0){
					write_count ++;
					}
				else {
					read_count ++;
					}
				send_flash_request(blkno*4,4,operation,1);
				blkno++;
				}
			break;
			}
		}

	  delay = calculate_delay_flash();

	  return delay;
}

